package com.brainydroid.datasend;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import org.apache.http.client.ClientProtocolException;



//=======
//<<<<<<< HEAD




public class CryptoStore {

	private static final String MAI_ID_FILENAME = "mai_id";
	//>>>>>>> ba6e1664898558547f95f3b266a71ced86412e5f
	private static final String PUBLIC_FILENAME = "key.pub";
	private static final String PRIVATE_FILENAME = "key";

	private static final String NOKP_EXCEPTION_MSG = "No keypair present";

	private static final String CURVE_NAME = "sect571k1";

	//<<<<<<< HEAD
	//=======
	private static File maiIdFile;
	//>>>>>>> ba6e1664898558547f95f3b266a71ced86412e5f
	private static File publicFile;
	private static File privateFile;
	private static File cacheDir;

	private static Crypto crypto;
	//<<<<<<< HEAD

	public static synchronized CryptoStore getInstance(File cd) {
		if (instance == null) {
			instance = new CryptoStore(cd);
		}
		return instance;
	}
	//=======
	private static ServerTalker servertalker;
	private static CryptoStore instance;

	public static synchronized CryptoStore getInstance(String s, File cd) {
		if (instance == null) {
			instance = new CryptoStore(s, cd);
			//>>>>>>> ba6e1664898558547f95f3b266a71ced86412e5f
		}

		return instance;
	}

	//<<<<<<< HEAD
	private CryptoStore(File cd) {
		crypto = Crypto.getInstance();
		cacheDir = cd;}
	//=======
	private CryptoStore(String s, File cd) {
		crypto = Crypto.getInstance();
		servertalker = ServerTalker.getInstance(s, this);
		cacheDir = cd;
		maiIdFile = new File(cacheDir, MAI_ID_FILENAME);
		//>>>>>>> ba6e1664898558547f95f3b266a71ced86412e5f
		publicFile = new File(cacheDir, PUBLIC_FILENAME);
		privateFile = new File(cacheDir, PRIVATE_FILENAME);
	}

	//<<<<<<< HEAD
	//=======
	private synchronized boolean hasStoredMaiId() {
		return maiIdFile.exists();
	}

	//>>>>>>> ba6e1664898558547f95f3b266a71ced86412e5f
	private synchronized boolean hasStoredPrivateKey() {
		return privateFile.exists();
	}

	private synchronized boolean hasStoredPublicKey() {
		return publicFile.exists();
	}

	//<<<<<<< HEAD
	public synchronized boolean hasStoredKeyPair() {
		if (hasStoredPrivateKey() && hasStoredPublicKey()) {
			return true;
		} else {
			clearPrivateKey();
			clearPublicKey();
			return false;
		}
	}
	//=======
	public synchronized boolean hasStoredKeyPairAndMaiId() {
		if (hasStoredPrivateKey() && hasStoredPublicKey() && hasStoredMaiId()) {
			return true;
		} else {
			clearStore();
			//>>>>>>> ba6e1664898558547f95f3b266a71ced86412e5f
			return false;
		}
	}

	public synchronized byte[] sign(byte[] data) {
		//<<<<<<< HEAD
		if (! hasStoredKeyPair()) {
			//=======
			if (! hasStoredKeyPairAndMaiId()) {
				//>>>>>>> ba6e1664898558547f95f3b266a71ced86412e5f
				throw new RuntimeException(NOKP_EXCEPTION_MSG);
			}
			try {
				return crypto.sign(getPrivateKey(), data);
			} catch (InvalidKeyException e) {
				throw new RuntimeException(e);
			}
		}else{
			return null;
		}
	}

	//<<<<<<< HEAD
	public synchronized boolean generateAndStoreKeyPair() {
		KeyPair kp = crypto.generateKeyPairNamedCurve(CURVE_NAME);
		return storeKeyPair(kp);
	}

	private synchronized boolean storeKeyPair(KeyPair kp) {
			return clearKeyPair();
		}
	

		//=======
		public synchronized boolean generateAndStoreKeyPairAndMaiId()
				throws ClientProtocolException, IOException {
			String maiId = servertalker.requestMaiId();
			KeyPair kp = crypto.generateKeyPairNamedCurve(CURVE_NAME);
			return storeKeyPairAndMaiId(kp, maiId);
		}

		private synchronized boolean storeKeyPairAndMaiId(KeyPair kp, String maiId) {
			try {
				clearStore();

				maiIdFile.createNewFile();
				BufferedWriter maiIdBuf = new BufferedWriter(new FileWriter(maiIdFile));
				maiIdBuf.write(maiId);
				maiIdBuf.close();

				//>>>>>>> ba6e1664898558547f95f3b266a71ced86412e5f
				publicFile.createNewFile();
				BufferedWriter publicBuf = new BufferedWriter(new FileWriter(publicFile));
				publicBuf.write(Crypto.base64Encode(kp.getPublic().getEncoded()));
				publicBuf.close();

				privateFile.createNewFile();
				BufferedWriter privateBuf = new BufferedWriter(new FileWriter(privateFile));
				privateBuf.write(Crypto.base64Encode(kp.getPrivate().getEncoded()));
				privateBuf.close();

				return true;
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		}

		public synchronized KeyPair getKeyPair() {
			return new KeyPair(getPublicKey(), getPrivateKey());
		}

		//<<<<<<< HEAD
		//=======
		public synchronized String getMaiId() {
			try {
				BufferedReader buf;
				buf = new BufferedReader(new FileReader(maiIdFile));
				String maiId = buf.readLine();
				buf.close();
				return maiId;
			} catch (FileNotFoundException e) {
				throw new RuntimeException(e);
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		}

		//>>>>>>> ba6e1664898558547f95f3b266a71ced86412e5f
		public synchronized PrivateKey getPrivateKey() {
			try {
				BufferedReader buf;
				buf = new BufferedReader(new FileReader(privateFile));
				String keyStr = buf.readLine();
				buf.close();
				return crypto.readPrivateKey(keyStr);
			} catch (FileNotFoundException e) {
				throw new RuntimeException(e);
			} catch (IOException e) {
				throw new RuntimeException(e);
			} catch (InvalidKeySpecException e) {
				throw new RuntimeException(e);
			}
		}

		public synchronized PublicKey getPublicKey() {
			try {
				BufferedReader buf;
				buf = new BufferedReader(new FileReader(publicFile));
				String keyStr = buf.readLine();
				buf.close();
				return crypto.readPublicKey(keyStr);
			} catch (FileNotFoundException e) {
				throw new RuntimeException(e);
			} catch (IOException e) {
				throw new RuntimeException(e);
			} catch (InvalidKeySpecException e) {
				throw new RuntimeException(e);
			}
		}

		//<<<<<<< HEAD
		public synchronized boolean clearKeyPair() {
			return clearPrivateKey() && clearPublicKey();}
		//=======
		public synchronized boolean clearStore() {
			return clearPrivateKey() && clearPublicKey() && clearMaiId();
		}

		private synchronized boolean clearMaiId() {
			return maiIdFile.delete();
			//>>>>>>> ba6e1664898558547f95f3b266a71ced86412e5f
		}

		private synchronized boolean clearPrivateKey() {
			return privateFile.delete();
		}

		private synchronized boolean clearPublicKey() {
			return publicFile.delete();
		}

		public synchronized File createArmoredPublicKeyFile() {
			File keyFile;
			try {
				keyFile = File.createTempFile("publicKey", ".pub", cacheDir);
				FileWriter keyFileWriter = new FileWriter(keyFile);
				keyFileWriter.write(getArmoredPublicKeyString());
				keyFileWriter.close();
				return keyFile;
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		}

		public synchronized String getArmoredPublicKeyString() {
			return Crypto.armorPublicKey(getPublicKey());
		}

		public synchronized String getPrivateKeyString() {
			return Crypto.base64Encode(getPrivateKey().getEncoded());
		}

		public synchronized SignedDataFiles createSignedDataFiles(String data) {
			try {
				byte[] signature = sign(data.getBytes());

				File dataFile = File.createTempFile("data",	".json", cacheDir);
				FileWriter dataFileWriter = new FileWriter(dataFile);
				dataFileWriter.write(data);
				dataFileWriter.close();

				File sigFile;
				sigFile = File.createTempFile("data", ".json.sig", cacheDir);
				BufferedOutputStream sigOut = new BufferedOutputStream(new FileOutputStream(sigFile));
				sigOut.write(signature);
				sigOut.close();

				SignedDataFiles sdf = new SignedDataFiles(dataFile, sigFile);
				return sdf;
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		}
	}
